package com.fangyaxun.jwttoken.utils;


import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.io.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;

/**
 *
 */
public class Example {
    //不同的key长度，会自动使用不同的加密算法
    private static String keyStrOther_32 = "abcj123defghi456klmntuvwxyzopqrs";
    private static String keyStr_32 = "abcdefghijklmnopqrstuvwxyz123456";
    private static String keyStr_48 = "abcdefghijklmnopqrstuvwxyz01234567890123456789ab";
    private static String keyStr_64 = "abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890123456789ab";

    /**
     * Keys.hmacShaKeyFor 会根据参数的字节长度，自动选择相应的算法
     * 根据RFC规范，不同的算法，对密钥的最小长度有要求。只有满足算法的最小长度要求，才足够安全
     *
     * 1>HmacSHA256 要求加密密钥的最小长度是:256bit=32byte, 即密钥长度最小要是32个字符串
     * 2>HmacSHA384 要求加密密钥的最小长度是:384bit=48byte, 即密钥长度最小要是48个字符串
     * 3>HmacSHA512 要求加密密钥的最小长度是:512bit=64byte, 即密钥长度最小要是64个字符串
     *
     */
    public static void testKeyMethod(){
        SecretKey secretKey_32 = Keys.hmacShaKeyFor(keyStr_32.getBytes());
        System.out.println("密钥的字节数 :"+keyStr_32.getBytes().length +"   密钥字串:" + keyStr_32 +"  自动选择的算法:"+secretKey_32.getAlgorithm());
        SecretKey secretKey_48 = Keys.hmacShaKeyFor(keyStr_48.getBytes());
        System.out.println("密钥的字节数 :"+keyStr_48.getBytes().length +"   密钥字串:" + keyStr_48 +"  自动选择的算法:"+secretKey_48.getAlgorithm());
        SecretKey secretKey_64 = Keys.hmacShaKeyFor(keyStr_64.getBytes());
        System.out.println("密钥的字节数 :"+keyStr_64.getBytes().length +"   密钥字串:" + keyStr_64 +"  自动选择的算法:"+secretKey_64.getAlgorithm());

        //以下为Keys.hmacShaKeyFor中内部选择不同key的实现
        final List<SignatureAlgorithm> PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList(
                SignatureAlgorithm.HS512, SignatureAlgorithm.HS384, SignatureAlgorithm.HS256));
        byte[] keyBytes = "abcdefghijklmnopqrstuvwxyz123456".getBytes();
        System.out.println(keyBytes.length*8);
        for (SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) {
            System.out.println("name:"+ alg.getJcaName() +" minKeylength:" + alg.getMinKeyLength());
        }
    }

    /**
     * 我们将密钥放在配置文件中，通过属性配置读取进来
     */
    public static void createSafeToken(){
        SecretKey secretKey_32 = Keys.hmacShaKeyFor(keyStr_32.getBytes());

        String jws = Jwts.builder()
                .setId("1222322121123123")
                .setHeaderParam("token","mytoken")
                .setSubject("Joe")
                .setAudience("how are you")
                .claim("userid","123456")
                .signWith(secretKey_32).compact();

        SecretKey secretKey_error = Keys.hmacShaKeyFor(keyStrOther_32.getBytes());
        //用错误的密码来验证，产生异常表示不匹配
        Jwts.parser().setSigningKey(secretKey_error).parseClaimsJws(jws);

    }

    public static void main(String[] args) {
        testKeyMethod();
        System.out.println("\n");
        createSafeToken();
//        safeKey();
//        testkey();
//        createToken();
//        testkey();
    }


    public static void safeKey(){
        SecretKey key = Keys.hmacShaKeyFor(keyStr_32.getBytes());

        System.out.println(key.getClass().getCanonicalName());


        System.out.println(key.getAlgorithm());
        System.out.println(key.getFormat());
        System.out.println(new String(Base64.getEncoder().encode(key.getEncoded())));

        String jws = Jwts.builder()
                .setId("1222322121123123")
                .setHeaderParam("token","mytoken")
                .setSubject("Joe")
                .setAudience("how are you")
                .claim("userid","123456")
                .signWith(key).compact();

        System.out.println(jws);
        String[] parts = jws.split("\\.");
        System.out.println(new String(Base64.getDecoder().decode(parts[0])));
        System.out.println(new String(Base64.getDecoder().decode(parts[1])));
        System.out.println(parts[2]);

    }

    public static void  createToken(){
        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String jws = Jwts.builder()
                .setId("1222322121123123")
                .setHeaderParam("token","mytoken")
                .setSubject("Joe")
                .setAudience("how are you")
                .claim("userid","123456")
                .signWith(key).compact();

        System.out.println(jws);
        String[] parts = jws.split("\\.");
        System.out.println(new String(Base64.getDecoder().decode(parts[0])));
        System.out.println(new String(Base64.getDecoder().decode(parts[1])));
        System.out.println(parts[2]);

    }

    /**
     * 检测密钥的安全性
     * 1>HmacSHA256 要求加密密钥的最小长度是:256bit=32byte, 即密钥长度最小要是32个字符串
     * 2>HmacSHA384 要求加密密钥的最小长度是:384bit=48byte, 即密钥长度最小要是48个字符串
     * 3>HmacSHA512 要求加密密钥的最小长度是:512bit=64byte, 即密钥长度最小要是64个字符串
     */
    public static void testkey(){
        String keyStr_32 = "abcdefghijklmnopqrstuvwxyz123456";
        String keyStr_48 = "abcdefghijklmnopqrstuvwxyz01234567890123456789ab";
        String keyStr_64 = "abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890123456789ab";

        System.out.println(keyStr_32.length());
        System.out.println(keyStr_48.length());
        System.out.println(keyStr_64.length());

        final List<SignatureAlgorithm> PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList(
                SignatureAlgorithm.HS512, SignatureAlgorithm.HS384, SignatureAlgorithm.HS256));
        byte[] keyBytes = "abcdefghijklmnopqrstuvwxyz123456".getBytes();
        System.out.println(keyBytes.length*8);
        for (SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) {
            System.out.println("name:"+ alg.getJcaName() +" minKeylength:" + alg.getMinKeyLength());
        }

    }

    public static void verifyToken(){

        try {
            //这里会根据keyBytes 长度自动选择用Hmac256或者
            SecretKey secretKey = Keys.hmacShaKeyFor(keyStr_32.getBytes());
            System.out.println(secretKey.getAlgorithm());
            System.out.println(new String(secretKey.getEncoded()));

            Keys.secretKeyFor(SignatureAlgorithm.HS384);

            String jws = Jwts.builder()
                    .setId("1222322121123123")
                    .setHeaderParam("token","mytoken")
                    .setSubject("Joe")
                    .setAudience("how are you")
                    .claim("userid","123456")
                    .signWith(secretKey).compact();

            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jws);

            //OK, we can trust this JWT

        } catch (JwtException e) {
            e.printStackTrace();
            //don't trust the JWT!
        }
    }


}
